<!doctype html>
<html lang="en">
	<head>
		<title>Face deformation</title>
		<meta charset="utf-8">
		<link href="./styles/bootstrap.min.css" rel="stylesheet" type="text/css">
		<style>
			@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);

			body {
				font-family: 'Lato';
				background-color: #f0f0f0;
				margin: 0px auto;
				max-width: 1150px;
			}

			#overlay, #canvasel {
				position: absolute;
				top: 0px;
				left: 0px;
				-o-transform : scaleX(-1);
				-webkit-transform : scaleX(-1);
				transform : scaleX(-1);
				-ms-filter : fliph; /*IE*/
				filter : fliph; /*IE*/
			}

			#videoel {
				-o-transform : scaleX(-1);
				-webkit-transform : scaleX(-1);
				transform : scaleX(-1);
				-ms-filter : fliph; /*IE*/
				filter : fliph; /*IE*/
			}

			#container, #webglcontainer {
				position : relative;
				width : 370px;
				/*margin : 0px auto;*/
			}

			#content {
				margin-top : 70px;
				margin-left : 100px;
				margin-right : 100px;
				max-width: 950px;
			}

			h2 {
				font-weight : 400;
			}

			.nogum {
				display : none;
			}

			.btn {
				font-family: 'Lato';
				font-size: 16px;
			}

			.hide {
				display : none;
			}

			.nohide {
				display : block;
			}
		</style>
		<script>
			// getUserMedia only works over https in Chrome 47+, so we redirect to https. Also notify user if running from file.
			if (window.location.protocol == "file:") {
				alert("You seem to be running this example directly from a file. Note that these examples only work when served from a server or localhost due to canvas cross-domain restrictions.");
			} else if (window.location.hostname !== "localhost" && window.location.protocol !== "https:"){
				window.location.protocol = "https";
			}
		</script>
		<script type="text/javascript">

			var _gaq = _gaq || [];
			_gaq.push(['_setAccount', 'UA-32642923-1']);
			_gaq.push(['_trackPageview']);

			(function() {
				var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
				ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
				var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
			})();

		</script>
	</head>
	<body>
		<script src="./js/libs/dat.gui.min.js"></script>
		<script src="./js/libs/utils.js"></script>
		<script src="./js/libs/webgl-utils.js"></script>
		<script src="../build/clmtrackr.js"></script>
		<script src="../models/model_pca_20_svm.js"></script>
		<script src="./js/libs/Stats.js"></script>
		<script src="./js/face_deformer.js"></script>
		<div id="content">
			<h2>Face deformation</h2>
			<div id="container" class="nohide">
				<video id="videoel" width="370" height="288" preload="auto" playsinline autoplay></video>
				<canvas id="canvasel" width="370" height="288" class="hide"></canvas>
				<canvas id="overlay" width="370" height="288"></canvas>
			</div>
			<div id="webglcontainer" class="hide">
				<canvas id="webgl" width="306" height="342"></canvas>
			</div>
			<br/>
			<div id="buttons">
				<input class="btn" type="button" value="wait, loading video" disabled="disabled" onclick="startVideo()" id="startbutton"></input>
			</div>
			<p id="score"></p>
			<div id="text">
				<p>This is an example of face deformation using the javascript library <a href="https://github.com/auduno/clmtrackr"><em>clmtrackr</em></a>.</p>
				<p>Note that this example needs support for WebGL, and works best in Google Chrome.</p>
				<div id="gum" class="nohide">
					<p>To try it out:
					<ol>
						<li>allow the page to your use webcamera</li>
						<li>make sure that your face is clearly visible in the video, and click start</li>
						<li>keep your head still until the model fits your face properly</li>
						<li>choose a preset or fiddle with the parameters on the right hand side, to deform the captured face</li>
					<ol>
					</p>
				</div>
				<div id="nogum" class="hide">
					<p>
						There was some problem trying to capture your webcamera, please check that your browser supports WebRTC. Instead we'll use a static image. To try it out:
						<ol>
						<li>click start</li>
						<li>wait till the model fits the face</li>
						<li>choose a preset or fiddle with the parameters on the right hand side, to deform the captured face</li>
						</ol>
					</p>
				</div>
			</div>
			<a href="https://github.com/auduno/clmtrackr"><img style="position: absolute; top: 0; left: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_left_green_007200.png" alt="Fork me on GitHub"></a>
			<p id='gUMMessage'></p>
			<script>
				var vid = document.getElementById('videoel');
				var vid_width = vid.width;
				var vid_height = vid.height;
				var overlay = document.getElementById('overlay');
				var overlayCC = overlay.getContext('2d');

				/*********** Setup of video/webcam and checking for webGL support *********/

				function enablestart() {
					var startbutton = document.getElementById('startbutton');
					startbutton.value = "start";
					startbutton.disabled = null;
				}

				var insertAltVideo = function(video) {
					// insert alternate video if getUserMedia not available
					if (supports_video()) {
						if (supports_webm_video()) {
							video.src = "./media/franck.webm";
						} else if (supports_h264_baseline_video()) {
							video.src = "./media/franck.mp4";
						} else {
							return false;
						}
						fd.init(document.getElementById('webgl'));
						return true;
					} else return false;
				}

				var insertAltImage = function() {
					var canvas = document.getElementById('canvasel');
					var cc = canvas.getContext('2d');
					var img = new Image();
					img.onload = function() {
						cc.drawImage(img, 0, 0, vid_width, vid_height);
					};
					img.src = './media/franck_02221.jpg';
					vid.className = "hide";
					vid = canvas;
					canvas.className = "nohide";

					var startbutton = document.getElementById('startbutton');
					startbutton.onclick = function() {
						ctrack.start(vid);
						drawLoop();
					}
					startbutton.value = "start";
					startbutton.disabled = null;
				}

				function adjustVideoProportions() {
					// resize overlay and video if proportions are not 4:3
					// keep same height, just change width
					var proportion = vid.videoWidth/vid.videoHeight;
					vid_width = Math.round(vid_height * proportion);
					vid.width = vid_width;
					overlay.width = vid_width;
				}

				// check whether browser supports webGL
				var webGLContext;
				var webGLTestCanvas = document.createElement('canvas');
				if (window.WebGLRenderingContext) {
					webGLContext = webGLTestCanvas.getContext('webgl') || webGLTestCanvas.getContext('experimental-webgl');
					if (!webGLContext || !webGLContext.getExtension('OES_texture_float')) {
						webGLContext = null;
					}
				}
				if (webGLContext == null) {
					alert("Your browser does not seem to support WebGL. Unfortunately this face deformation example depends on WebGL, so you'll have to try it in another browser. :(");
				}

				function gumSuccess( stream ) {
					// add camera stream if getUserMedia succeeded
					if ("srcObject" in vid) {
						vid.srcObject = stream;
					} else {
						vid.src = (window.URL && window.URL.createObjectURL(stream));
					}
					vid.onloadedmetadata = function() {
						adjustVideoProportions();
						fd.init(document.getElementById('webgl'));
						vid.play();
					}
					vid.onresize = function() {
						adjustVideoProportions();
						fd.init(document.getElementById('webgl'));
						if (trackingStarted) {
							ctrack.stop();
							ctrack.reset();
							ctrack.start(vid);
						}
					}
				}

				function gumFail() {
					// fall back to video if getUserMedia failed
					insertAltVideo(vid);
					document.getElementById('gum').className = "hide";
					document.getElementById('nogum').className = "nohide";
					alert("There was some problem trying to fetch video from your webcam, using a static image instead.");
				}

				navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
				window.URL = window.URL || window.webkitURL || window.msURL || window.mozURL;

				// check for camerasupport
				if (navigator.mediaDevices) {
					navigator.mediaDevices.getUserMedia({video : true}).then(gumSuccess).catch(gumFail);
				} else if (navigator.getUserMedia) {
					navigator.getUserMedia({video : true}, gumSuccess, gumFail);
				} else {
					insertAltImage();
					//insertAltVideo(vid);
					document.getElementById('gum').className = "hide";
					document.getElementById('nogum').className = "nohide";
					alert("Your browser does not seem to support getUserMedia, using a static image instead.");
				}

				vid.addEventListener('canplay', enablestart, false);

				/*********** Code for face tracking and face deformation *********/

				var ctrack = new clm.tracker();
				ctrack.init(pModel);
				var trackingStarted = false;

				function startVideo() {
					// start video
					vid.play();
					// start tracking
					ctrack.start(vid);
					trackingStarted = true;
					//start drawing faces
					drawLoop();
				}

				var positions;
				var fd = new faceDeformer();

				function drawLoop() {
					// track in video
					positions = ctrack.getCurrentPosition();
					overlayCC.clearRect(0, 0, vid_width, vid_height);
					if (positions) {
						ctrack.draw(overlay);
					}

					// hide buttons
					elem = document.getElementById('buttons');
					elem.setAttribute('class', 'hide');
					// show message
					var scoreel = document.getElementById('score');
					scoreel.innerHTML = "Please keep head still while model fits";

					var pn = ctrack.getConvergence();
					if (pn < 1.8) {
						requestAnimFrame(setupFaceDeformation);
						scoreel.innerHTML = "";
						if (vid.tagName == "VIDEO") {
							vid.pause();
						}
					} else {
						requestAnimFrame(drawLoop);
					}
				}

				var ph, gui;
				var rotation = 0;
				var scale = 3;
				var xOffset = -10;
				var yOffset = 0;

				function setupFaceDeformation() {
					// draw face deformation model
					positions = ctrack.getCurrentPosition();
					overlayCC.clearRect(0, 0, vid_width, vid_height);
					ctrack.draw(overlay);
					fd.load(vid, positions, pModel);
					fd.draw(positions);

					// hide video
					var elem = document.getElementById('container');
					elem.setAttribute('class', 'hide');
					// show facial deformation element
					elem = document.getElementById('webglcontainer');
					elem.setAttribute('class', 'nohide');
					// hide message element
					document.getElementById('score').setAttribute('class', 'hide');

					// set up controls
					ph = new parameterHolder();
					gui = new dat.GUI();
					var guiSelect = gui.add(ph, 'presets', presets);
					var gui1 = gui.add(ph, 'param1', -50, 50).listen();
					var gui2 = gui.add(ph, 'param2', -20, 20).listen();
					var gui3 = gui.add(ph, 'param3', -20, 20).listen();
					var gui4 = gui.add(ph, 'param4', -20, 20).listen();
					var gui5 = gui.add(ph, 'param5', -20, 20).listen();
					var gui6 = gui.add(ph, 'param6', -20, 20).listen();
					var gui7 = gui.add(ph, 'param7', -20, 20).listen();
					var gui8 = gui.add(ph, 'param8', -20, 20).listen();
					var gui9 = gui.add(ph, 'param9', -20, 20).listen();
					var gui10 = gui.add(ph, 'param10', -20, 20).listen();
					var gui11 = gui.add(ph, 'param11', -20, 20).listen();
					var gui12 = gui.add(ph, 'param12', -20, 20).listen();
					var gui13 = gui.add(ph, 'param13', -20, 20).listen();
					var gui14 = gui.add(ph, 'param14', -20, 20).listen();
					var gui15 = gui.add(ph, 'param15', -20, 20).listen();
					var gui16 = gui.add(ph, 'param16', -20, 20).listen();
					var gui17 = gui.add(ph, 'param17', -20, 20).listen();
					var gui18 = gui.add(ph, 'param18', -20, 20).listen();
					var gui19 = gui.add(ph, 'param19', -20, 20).listen();
					var gui20 = gui.add(ph, 'param20', -20, 20).listen();
					var guiGrid = gui.add(ph, 'draw_grid', false);
					var guiFace = gui.add(ph, 'draw_face', true);

					gui1.onChange(drawDeformedFace);
					gui2.onChange(drawDeformedFace);
					gui3.onChange(drawDeformedFace);
					gui4.onChange(drawDeformedFace);
					gui5.onChange(drawDeformedFace);
					gui6.onChange(drawDeformedFace);
					gui7.onChange(drawDeformedFace);
					gui8.onChange(drawDeformedFace);
					gui9.onChange(drawDeformedFace);
					gui10.onChange(drawDeformedFace);
					gui11.onChange(drawDeformedFace);
					gui12.onChange(drawDeformedFace);
					gui13.onChange(drawDeformedFace);
					gui14.onChange(drawDeformedFace);
					gui15.onChange(drawDeformedFace);
					gui16.onChange(drawDeformedFace);
					gui17.onChange(drawDeformedFace);
					gui18.onChange(drawDeformedFace);
					gui19.onChange(drawDeformedFace);
					gui20.onChange(drawDeformedFace);
					guiSelect.onChange(switchDeformedFace);
					guiGrid.onChange(drawDeformedFace);
					guiFace.onChange(drawDeformedFace);

					drawDeformedFace();
				}

				function drawDeformedFace() {
					// draw model
					var parameters = ctrack.getCurrentParameters();
					parameters[0] = scale*Math.cos(rotation)-1;
					parameters[1] = scale*Math.sin(rotation);
					parameters[2] = xOffset;
					parameters[3] = yOffset;
					parameters[0+4] = ph.param1;
					parameters[1+4] = ph.param2;
					parameters[2+4] = ph.param3;
					parameters[3+4] = ph.param4;
					parameters[4+4] = ph.param5;
					parameters[5+4] = ph.param6;
					parameters[6+4] = ph.param7;
					parameters[7+4] = ph.param8;
					parameters[8+4] = ph.param9;
					parameters[9+4] = ph.param10;
					parameters[10+4] = ph.param11;
					parameters[11+4] = ph.param12;
					parameters[12+4] = ph.param13;
					parameters[13+4] = ph.param14;
					parameters[14+4] = ph.param15;
					parameters[15+4] = ph.param16;
					parameters[16+4] = ph.param17;
					parameters[17+4] = ph.param18;
					parameters[18+4] = ph.param19;
					parameters[19+4] = ph.param20;
					positions = fd.calculatePositions(parameters, true);
					fd.clear();
					if (ph.draw_face) fd.draw(positions);
					if (ph.draw_grid) fd.drawGrid(positions);
				}

				function switchDeformedFace() {
					// draw model
					var parameters = ctrack.getCurrentParameters();
					parameters[0] = scale*Math.cos(rotation)-1;
					parameters[1] = scale*Math.sin(rotation);
					parameters[2] = xOffset;
					parameters[3] = yOffset;
					var split = ph.presets.split(",");
					parameters[0+4] = ph.param1 = parseInt(split[0],10);
					parameters[1+4] = ph.param2 = parseInt(split[1],10);
					parameters[2+4] = ph.param3 = parseInt(split[2],10);
					parameters[3+4] = ph.param4 = parseInt(split[3],10);
					parameters[4+4] = ph.param5 = parseInt(split[4],10);
					parameters[5+4] = ph.param6 = parseInt(split[5],10);
					parameters[6+4] = ph.param7 = parseInt(split[6],10);
					parameters[7+4] = ph.param8 = parseInt(split[7],10);
					parameters[8+4] = ph.param9 = parseInt(split[8],10);
					parameters[9+4] = ph.param10 = parseInt(split[9],10);
					parameters[10+4] = ph.param11 = parseInt(split[10],10);
					parameters[11+4] = ph.param12 = parseInt(split[11],10);
					parameters[12+4] = ph.param13 = parseInt(split[12],10);
					parameters[13+4] = ph.param14 = parseInt(split[13],10);
					parameters[14+4] = ph.param15 = parseInt(split[14],10);
					parameters[15+4] = ph.param16 = parseInt(split[15],10);
					parameters[16+4] = ph.param17 = parseInt(split[16],10);
					parameters[17+4] = ph.param18 = parseInt(split[17],10);
					parameters[18+4] = ph.param19 = parseInt(split[18],10);
					parameters[19+4] = ph.param20 = parseInt(split[19],10);
					positions = fd.calculatePositions(parameters, true);
					if (ph.draw_face) fd.draw(positions);
					if (ph.draw_grid) fd.drawGrid(positions);
				}

				var parameterHolder = function() {
					this.param1 = 0.0001;
					this.param2 = 0.0001;
					this.param3 = 0.0001;
					this.param4 = 0.0001;
					this.param5 = 0.0001;
					this.param6 = 0.0001;
					this.param7 = 0.0001;
					this.param8 = 0.0001;
					this.param9 = 0.0001;
					this.param10 = 0.0001;
					this.param11 = 0.0001;
					this.param12 = 0.0001;
					this.param13 = 0.0001;
					this.param14 = 0.0001;
					this.param15 = 0.0001;
					this.param16 = 0.0001;
					this.param17 = 0.0001;
					this.param18 = 0.0001;
					this.param19 = 0.0001;
					this.param20 = 0.0001;
					this.presets = 0;
					this.draw_face = true;
					this.draw_grid = false;
				}

				var presets = {
					"default" : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
					"oi mate" : [0, 0, 13, 1.2, 0, -15, 0, 1, 8, -5, 0, 0, 0, 0, 0, 0, 0, 11.6, 0, -7],
					"unhappy" : [0, 0, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
					"greek" : [0, 0, 0, 1.6, 0, -6, 0, 0, 0, -13, 0, 4.7, 1, 0, 11, -1, 8, 8, 0, 0],
					"cheery" : [0, 0, 0, 0, 10.7, 0, 16.8, 0, 0, -5, 0, -4, 13, 0, 0, 0, 0, 0, 0, 0],
					"luke" : [0, 0, -1.7, -8.7, -8, -4.8, 12.5, -1, 14.6, -11, 0, -2, -13, 0, 0, 0, 0, 7, 0, -3],
					"chum" : [0, 0, 13, 1.2, 0, 2.5, 0, 1, 16.8, -5, 0, 0, 0, 0, 0, 0, 0, 11.6, 0, -7],
					"disgust" : [-4, -14, 8, 2, 3, -5.6, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10, 0, -5],
				};

				/*********** Code for stats **********/

				stats = new Stats();
				stats.domElement.style.position = 'absolute';
				stats.domElement.style.top = '0px';
				document.getElementById('container').appendChild( stats.domElement );

				document.addEventListener("clmtrackrIteration", function(event) {
					stats.update();
				}, false);

			</script>
		</div>
	</body>
</html>
